package com.mlethe.library.recyclerview.callback;

import android.graphics.Canvas;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

import com.mlethe.library.recyclerview.adapter.BaseDraggableAdapter;
import com.mlethe.library.recyclerview.adapter.UniversalAdapter;

import java.util.List;

/**
 * item 滑动删除和拖拽回调
 *
 * @author Mlethe
 * @date 2020/9/19
 */
public class ItemDragAndSwipeCallback extends ItemTouchHelper.Callback {

    private BaseDraggableAdapter mAdapter;
    private float mMoveThreshold = 0.5f;
    private float mSwipeThreshold = 0.5f;

    private int mDragMoveFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
    private int mSwipeMoveFlags = ItemTouchHelper.END;

    /**
     * 操作，0 滑动删除，1 拖拽
     */
    private int mAction = -1;

    public ItemDragAndSwipeCallback(BaseDraggableAdapter adapter) {
        this.mAdapter = adapter;
    }

    private boolean isViewCreateByAdapter(RecyclerView.ViewHolder viewHolder) {
        if (viewHolder == null) {
            return true;
        }
        int type = viewHolder.getItemViewType();
        return mAdapter.isHeaderViewType(type) || type == UniversalAdapter.LOADING_VIEW
                || mAdapter.isFooterViewType(type) || type == UniversalAdapter.EMPTY_VIEW;
    }

    /**
     * 这个方法用于让RecyclerView拦截向上滑动，向下滑动，向左滑动
     * makeMovementFlags(dragFlags, swipeFlags);dragFlags是上下方向的滑动 swipeFlags是左右方向上的滑动
     * <p>
     * dragFlags 和 swipeFlags 某个值传0，表示不触发该操作
     *
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        if (isViewCreateByAdapter(viewHolder)) {
            return makeMovementFlags(0, 0);
        }
        return makeMovementFlags(getDragMoveFlags(recyclerView, viewHolder), getSwipeMoveFlags(recyclerView, viewHolder));
    }

    /**
     * 获取拖拽移动方向
     *
     * @return 拖拽移动方向
     */
    public int getDragMoveFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        return mDragMoveFlags;
    }

    /**
     * 获取滑动删除移动方向
     *
     * @return 滑动删除移动方向
     */
    public int getSwipeMoveFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        return mSwipeMoveFlags;
    }

    /**
     * 针对swipe和drag状态，当swipe或者drag对应的ViewHolder改变的时候调用
     * 我们可以通过重写这个函数获取到swipe、drag开始和结束时机，viewHolder 不为空的时候是开始，空的时候是结束
     *
     * @param viewHolder
     * @param actionState 当前Item的状态
     *                    {@link ItemTouchHelper#ACTION_STATE_IDLE}   闲置状态
     *                    {@link ItemTouchHelper#ACTION_STATE_SWIPE}  滑动中状态
     *                    {@link ItemTouchHelper#ACTION_STATE_DRAG}   拖拽中状态
     */
    @Override
    public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
        boolean isViewCreateByAdapter = isViewCreateByAdapter(viewHolder);
        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && !isViewCreateByAdapter) {
            mAction = 1;
            mAdapter.onItemDragStart(viewHolder);
        } else if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && !isViewCreateByAdapter) {
            mAction = 0;
            mAdapter.onItemSwipeStart(viewHolder);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * 针对swipe和drag状态，当一个item view在swipe、drag状态结束的时候调用
     * drag状态：当手指释放的时候会调用
     * swipe状态：当item从RecyclerView中删除的时候调用，一般我们会在onSwiped()函数里面删除掉指定的item view
     */
    @Override
    public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (isViewCreateByAdapter(viewHolder)) {
            return;
        }
        if (mAction == 0) {
            mAction = -1;
            if (mAdapter != null) {
                mAdapter.onItemSwipeClear(recyclerView, viewHolder);
            }
        } else if (mAction == 1) {
            mAction = -1;
            if (mAdapter != null) {
                mAdapter.onItemDragEnd(recyclerView, viewHolder);
            }
        }
    }

    /**
     * 针对swipe和drag状态，整个过程中一直会调用这个函数(和ItemDecoration里面的onDrawOver()函数对应)
     * 这个函数提供给我们可以在RecyclerView的上面再绘制一层东西，比如绘制一层蒙层啥的
     */
    @Override
    public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
                                float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && !isViewCreateByAdapter(viewHolder)) {
            View itemView = viewHolder.itemView;

            c.save();
            if (dX > 0) {
                c.clipRect(itemView.getLeft(), itemView.getTop(),
                        itemView.getLeft() + dX, itemView.getBottom());
                c.translate(itemView.getLeft(), itemView.getTop());
            } else {
                c.clipRect(itemView.getRight() + dX, itemView.getTop(),
                        itemView.getRight(), itemView.getBottom());
                c.translate(itemView.getRight() + dX, itemView.getTop());
            }
            if (mAdapter != null) {
                mAdapter.onItemSwiping(c, viewHolder, dX, dY, isCurrentlyActive);
            }
            c.restore();

        }
    }

    /**
     * 针对swipe和drag状态，整个过程中一直会调用这个函数,随手指移动的view就是在super里面做到的(和ItemDecoration里面的onDraw()函数对应)
     * <p>
     * 移动过程中绘制Item
     *
     * @param c
     * @param recyclerView
     * @param viewHolder
     * @param dX                X轴移动的距离
     * @param dY                Y轴移动的距离
     * @param actionState       当前Item的状态
     * @param isCurrentlyActive 如果当前被用户操作为true，反之为false
     */
    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

    /**
     * 针对swipe和drag状态，当手指离开之后，view回到指定位置动画的持续时间(swipe可能是回到原位，也有可能是swipe掉)
     */
    @Override
    public long getAnimationDuration(@NonNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
        return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
    }

    /**
     * 针对drag状态，Item是否支持长按拖动
     *
     * @return true 支持长按操作   false 不支持长按操作
     */
    @Override
    public boolean isLongPressDragEnabled() {
        if (mAdapter != null) {
            return mAdapter.isItemDragEnabled();
        }
        return false;
    }

    /**
     * drag状态下，在canDropOver()返回true时，会调用该方法让我们拖动换位置的逻辑(需要自己处理变换位置的逻辑)
     */
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
        return viewHolder.getItemViewType() == target.getItemViewType();
    }

    /**
     * 当onMove return true的时候调用(一般不用我们自己处理，直接super就好)
     */
    @Override
    public void onMoved(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, int fromPos, @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
        super.onMoved(recyclerView, source, fromPos, target, toPos, x, y);
        if (mAdapter != null) {
            mAdapter.onItemDragMoving(recyclerView, source, fromPos, target, toPos);
        }
    }

    /**
     * 针对drag状态，当滑动超过多少就可以出发onMove()方法(这里指onMove()方法的调用，并不是随手指移动的View)
     */
    @Override
    public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return mMoveThreshold;
    }

    /**
     * 针对drag状态，当前target对应的item是否允许移动
     * 我们一般用drag来做一些换位置的操作，就是当前对应的target对应的Item可以移动
     */
    @Override
    public boolean canDropOver(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder current, @NonNull RecyclerView.ViewHolder target) {
        return super.canDropOver(recyclerView, current, target);
    }

    /**
     * 针对drag状态，当drag ItemView跟底下ItemView重叠时，可以给drag ItemView设置一个Margin值
     * 让重叠不容易发生，相当于增大了drag Item的区域
     */
    @Override
    public int getBoundingBoxMargin() {
        return super.getBoundingBoxMargin();
    }

    /**
     * 针对drag状态，在drag的过程中获取drag itemView底下对应的ViewHolder(一般不用我们处理直接super就好了)
     */
    @Override
    public RecyclerView.ViewHolder chooseDropTarget(@NonNull RecyclerView.ViewHolder selected, @NonNull List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {
        return super.chooseDropTarget(selected, dropTargets, curX, curY);
    }

    /**
     * 针对drag状态，当itemView滑动到RecyclerView边界的时候(比如下面边界的时候),RecyclerView会scroll，
     * 同时会调用该函数去获取scroller距离(不用我们处理 直接super)
     */
    @Override
    public int interpolateOutOfBoundsScroll(@NonNull RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, int totalSize, long msSinceStartScroll) {
        return super.interpolateOutOfBoundsScroll(recyclerView, viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll);
    }

    /**
     * 针对swipe状态，Item是否支持滑动
     *
     * @return true 支持滑动操作     false 不支持滑动操作
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        if (mAdapter != null) {
            return mAdapter.isItemSwipeEnabled();
        }
        return false;
    }

    /**
     * 针对swipe状态，swipe 到达滑动消失的距离回调函数,一般在这个函数里面处理删除item的逻辑
     * 确切的来讲是swipe item滑出屏幕动画结束的时候调用
     *
     * @param viewHolder
     * @param direction  Item滑动的方向
     */
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        if (!isViewCreateByAdapter(viewHolder)) {
            if (mAdapter != null) {
                mAdapter.onItemSwiped(viewHolder);
            }
        }
    }

    /**
     * 针对swipe状态，swipe滑动的位置超过了百分之多少就消失
     */
    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return mSwipeThreshold;
    }

    /**
     * 针对swipe状态，swipe的逃逸速度，换句话说就算没达到getSwipeThreshold设置的距离，达到了这个逃逸速度item也会被swipe消失掉
     */
    @Override
    public float getSwipeEscapeVelocity(float defaultValue) {
        return super.getSwipeEscapeVelocity(defaultValue);
    }

    /**
     * 针对swipe状态，swipe滑动的阻尼系数,设置最大滑动速度
     */
    @Override
    public float getSwipeVelocityThreshold(float defaultValue) {
        return super.getSwipeVelocityThreshold(defaultValue);
    }

    /**
     * Set the fraction that the user should move the View to be considered as swiped.
     * The fraction is calculated with respect to RecyclerView's bounds.
     * <p>
     * Default value is .5f, which means, to swipe a View, user must move the View at least
     * half of RecyclerView's width or height, depending on the swipe direction.
     *
     * @param swipeThreshold A float value that denotes the fraction of the View size. Default value
     *                       is .8f .
     */
    public ItemDragAndSwipeCallback setSwipeThreshold(float swipeThreshold) {
        mSwipeThreshold = swipeThreshold;
        return this;
    }


    /**
     * Set the fraction that the user should move the View to be considered as it is
     * dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views
     * below it for a possible drop.
     *
     * @param moveThreshold A float value that denotes the fraction of the View size. Default value is
     *                      .1f .
     */
    public ItemDragAndSwipeCallback setMoveThreshold(float moveThreshold) {
        mMoveThreshold = moveThreshold;
        return this;
    }

    /**
     * <p>Set the drag movement direction.</p>
     * <p>The value should be ItemTouchHelper.UP, ItemTouchHelper.DOWN, ItemTouchHelper.LEFT, ItemTouchHelper.RIGHT or their combination.</p>
     * You can combine them like ItemTouchHelper.UP | ItemTouchHelper.DOWN, it means that the item could only move up and down when dragged.
     *
     * @param dragMoveFlags the drag movement direction. Default value is ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT.
     */

    public ItemDragAndSwipeCallback setDragMoveFlags(int dragMoveFlags) {
        mDragMoveFlags = dragMoveFlags;
        return this;
    }

    /**
     * <p>Set the swipe movement direction.</p>
     * <p>The value should be ItemTouchHelper.START, ItemTouchHelper.END or their combination.</p>
     * You can combine them like ItemTouchHelper.START | ItemTouchHelper.END, it means that the item could swipe to both left or right.
     *
     * @param swipeMoveFlags the swipe movement direction. Default value is ItemTouchHelper.END.
     */
    public ItemDragAndSwipeCallback setSwipeMoveFlags(int swipeMoveFlags) {
        mSwipeMoveFlags = swipeMoveFlags;
        return this;
    }
}
